/*
    This file is part of libcloak.

    libcloak is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    libcloak is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with libcloak.  If not, see <http://www.gnu.org/licenses/>.

*/



#ifndef CLOAK_MAN
#define CLOAK_MAN

#include "Globals.h"
#include "Resource.h"
#include "Handler.h"
#include <boost/shared_ptr.hpp>
#include <boost/weak_ptr.hpp>


#define MAX_CACHE_SIZE 100;

/* l33t macro hax to add handlers to manager
 Note that this enforces a specific pattern in adding handlers,
 namely that you instantiate a new object, pass the _SAME_ path
 to both the constructor for that and addHandler, and never use
 that object again.

 If you can manage to do all of that, you don't need this macro.
*/
#define CLOAK_ADDHANDLER(PATH,CLASS,MANAGER) MANAGER->addHandler(PATH, new CLASS(PATH));
#define CloakRef boost::shared_ptr
#define CLOAK_CATCH_DEBUG catch(Cloak::Manager::Exception e){std::cerr<<e.getMessage()<<endl;}
#define CLOAK_CATCH_RETHROW catch(Cloak::Manager::Exception e){std::cerr<<e.getMessage()<<endl; throw;}
namespace Cloak
{
	using namespace std;
	using namespace boost;

	#ifdef NOTHREADS
	typedef int pthread_mutex_t;
	#endif


	typedef map<string, shared_ptr<struct Node> > ChildrenMap;

	/// Node represents a node in the entity tree.
	typedef struct Node
	{
		string name;
		ChildrenMap children;
		shared_ptr<Handler> handler;
	} Node;


	/** Main manager class

	\brief This class is the main manager for the entity hierarchy.
	**/
	class Manager
	{
	private:

		/// The root node of the entity tree.
		shared_ptr<Node> root;

		/// The map of prefixes for different file types
		/// This is a map of prefixDesignation -> prefixPath
		map<string, string> prefixMap;

		/// The cache map of strings to entities.
		map<string, boost::any> cache;

		/// Least recently used multimap
		/// Map of double (time w/ microsecs) to void pointer to
		/// weak_ptr to something.
		multimap<double, pair<string,void*> > cacheLRU;

		/// The maximum size of the cache, in entities.
		int max_cache_size;


		// Vector holding pointers to threads collecting garbage on this
		// manager.
		vector<pthread_t*> garbageThreads;


		pthread_mutex_t prefixMutex; // Mutex for prefix map
		pthread_mutex_t cacheMutex;  // Mutex for cache map
		pthread_mutex_t treeMutex;    // Mutex for entity tree
		pthread_mutex_t getEntMutex; // Mutex to make sure only one
					     // thread is in getEntity.




		/**
			\brief Prepare path for processing and make sure it is valid.

			This method tokenizes the path and turns it into a
			vector. It also checks to make sure it does not violate
			any conventions.

			@param path The path to vectorize
			@return The path as a vector of strings (PathVector).
		*/
		PathVector vectorizePath(const string &path);


		/**
			\brief Get the handler for the specified string

			@param path The path to find the handler for
			@return Shared pointer to the handler.
			@warning This is NOT thread-safe. Lock treeMutex before
			calling.
		*/
		shared_ptr<Handler> getHandler(const string &path);


		/**
			\brief Get the handler, subroutine

			@param subroot The root of the subtree we are searching
			@param best The most specific handler found so far
			@param bestwild The most specific wild handler found so far
			@param current Head of current path subvector
			@param end End of path vector
		*/
		shared_ptr<Handler> getHandler(shared_ptr<Node> subroot,
			shared_ptr<Handler> best,
			shared_ptr<Handler> bestwild,
			PathVector::iterator &current,
			PathVector::iterator end);

		/**
			\brief Parse a string into an attribute map

			@param attrs String to turn into an attribute map
			@param amap Attribute map to store everything in
			@note This does not delete the attributes in the map,
			therefore it can be used multiple times on the same map.
		*/
		void parseAttrs(const string &attrs, AttrMap &amap);

		/**
			\brief Internal function to debug tree recursively.

			Called by public interface debugTree/0.

			@param current The node currently being output
			@param depth The current depth in the tree
			@warning This is NOT thread safe. Lock treeMutex before
			calling.
		*/
		void debugTree(shared_ptr<Node> current, int depth);



		void lock_mutex(pthread_mutex_t *mutex);

		void unlock_mutex(pthread_mutex_t *mutex);


		static void* garbage_thread(void* data);
	public:

		Manager();
		virtual ~Manager();

		class Exception
		{
		private:
			int value;
			string message;

		public:
			const static int NOPATH = 1;
			const static int NODEF = 2;
			const static int NOTFOUND = 3;
			const static int NOTFOUNDWILD = 4;
			const static int BADPATH = 5;
			const static int BADATTR = 6;
			const static int BADTYPE = 7;
			const static int NOENT = 8;
			int getValue()
			{
				return value;
			};

			const string &getMessage()
			{
				return message;
			}

			Exception(int value, string message);
		};

		/**
			\brief The modes of garbage collection

			FULL - Eliminate all entries that are currently not in
			use.

			QUARTER_SPACE - Search through the first 25% of entries
			that have least recently been used. Eliminate all
			that are not in use.

			QUARTER_SLIDING - Search through all entries starting
			with the least recently used, stop when 25% of
			the entries have been eliminated or there are no more
			entries unchecked.
		*/
		enum GarbageMode {FULL, QUARTER_SPACE, QUARTER_SLIDING};


		/**
			\brief Add a handler to the tree

			This should never really be called manually.

			Use the macro CLOAK_ADDHANDLER(path_str, HandlerDerivedClassName, manager_ptr) instead.
			Pass it the string for the path (either const char or std::string), the name of a class derived from Cloak::Handler, and a pointer to your Cloak::Manager object.
		*/
		void addHandler(const string &path, Handler *handler);


		/**
			\brief Add a prefix to the prefix map and make it available to
			all Handlers.

			For example, if your config file says that images are
			stored in /usr/share/mygame/images, call:

			manager->addPrefix("images","/usr/share/mygame/images/");

			Then when one of your handlers wants to load an image
			from the image directory (such as "alien.png"), they
			load:

			manager->getPrefix("images") + "alien.png";

			@param prefix The prefix type to add to the map
			@param path The path for that prefix type.
			@note This is thread safe.
		*/
		void addPrefix(const string &prefix, const string &path);

		/**
			\brief Get a prefix from the prefix map.

			For more information on usage, see the documentation for
			addHandler.

			@param prefix The prefix designation to get
			@return The prefix path corresponding to the given entry
			@note This is thread safe.
		*/
		string getPrefix(const string &prefix);

		/**
			\brief Perform garbage collection

			@param mode The GarbageMode to use for collection.

			@note This is thread safe.
		*/
		void garbageCollect(GarbageMode mode);

		/**
			\brief Start a new thread that will do garbage
			collection.

			@param mode The GarbageMode to use.
			@param time Time (in milliseconds) to wait between
			garbage sweeps

			@note This can be called multiple times with separate
			modes and time values. For instance you can have one
			threat that does a QUARTER_SPACE sweep every second
			and another that does a FULL sweep every minute.
		*/
		void startGarbageThread(GarbageMode mode, int time);

		/**
			\brief Stop all currently running garbage threads.

			@note This will be called for you during Manager
			destruction.
		*/
		void stopGarbageThreads();

		/**
			\brief Get a CloakRef that points to the requested
			entity.

			This is the main method you will use to load your
			resources. After loading something for the first time
			it will be cached for future use.

			This method is thread safe, so you can load entities
			in two seperate threads at the same time.

			@note This is thread safe.
			@bug  Constraints were relaxed on getEntity,
			      ensure that it is still thread-safe.
		*/
		template<typename T>
		CloakRef<const T> getEntity(const string &path, const string &attrs)
		{

			//lock_mutex(&getEntMutex);

			// FIRST CHECK THE CACHE

			string cache_key = path+"{"+attrs+"}";

			// Done exception-happy code, lock cache and check.
			lock_mutex(&cacheMutex);

			// Check cache, if it is found unlock and return it.
			map<string, Entity>::iterator it = cache.find(cache_key);
			if(it != cache.end())
			{
				CloakRef<const T> ret;
				try
				{
					ret = any_cast<CloakRef<const T> >(it->second);;
				}
				catch(bad_any_cast &e)
				{
					unlock_mutex(&cacheMutex);
					unlock_mutex(&getEntMutex);

					string bad = "BADTYPE getEntity/2: The CACHE gave us a type of object you were not expecting. ";
					bad += e.what();
					throw Exception(Exception::BADTYPE, bad);
				}

				unlock_mutex(&cacheMutex);
				unlock_mutex(&getEntMutex);

				return ret;

			}

			// Not found in cache, unlock the cache and get the
			// entity from the handlers.
			unlock_mutex(&cacheMutex);


			// NOT FOUND IN CACHE, NOW FIND THE HANDLER

			lock_mutex(&treeMutex);

			shared_ptr<Handler> h;
			try{
			h = getHandler(path);
			}
			catch(...)
			{
				// Unlock the mutex and rethrow.
				unlock_mutex(&treeMutex);
				unlock_mutex(&getEntMutex);

				throw;
			}

			unlock_mutex(&treeMutex);

			AttrMap amap;
			parseAttrs(attrs, amap);

			PathVector pvec = vectorizePath(path);

			Entity ent = h->handlePath(*this, pvec, amap);

			T *ret;
			try
			{
				ret = any_cast<T*>(ent);;
			}
			catch(bad_any_cast &e)
			{
				unlock_mutex(&getEntMutex);

				string bad = "BADTYPE getEntity/2: The HANDLER gave us a type of object you were not expecting. ";
				bad += e.what();
				throw Exception(Exception::BADTYPE, bad);
			}

			if(ret == NULL)
			{
				unlock_mutex(&getEntMutex);

				string bad = "NOENT getEntity/2: The handler gave us a null value.";
				throw Exception(Exception::NOENT, bad);
			}

			// We got the actual entity, put it in the cache.

			CloakRef<const T> cref(ret);

			lock_mutex(&cacheMutex);
			cache.insert(make_pair(cache_key,boost::any(cref)));

			// Insert into the LRU list
			timeval tv;
			gettimeofday(&tv, NULL);
			double key = tv.tv_sec + (tv.tv_usec / 1000000.0);


			weak_ptr<const T> *lref = new weak_ptr<const T>(cref);

			cacheLRU.insert(make_pair(key, make_pair(cache_key,(void*)lref)));
			unlock_mutex(&cacheMutex);

			//unlock_mutex(&getEntMutex);
			return cref;
		}

		/**
			\brief Get a CloakRef that points to the requested
			mutable entity.

			The returned entity is owned by the caller, and can be
			modified.

			It will automatically be deleted when the last CloakRef
			to it is destroyed.

			This is the method you will use to obtain a private copy
			of an entity so that you may modify it. Returned
			entities are not cached. Use this method if you want
			to use Cloak as a Simple Factory.

			@note This is thread safe.

		*/
		template<typename T>
		CloakRef<T> getMutableEntity(const string &path, const string &attrs)
		{
			lock_mutex(&treeMutex);

			shared_ptr<Handler> h;
			try{
			h = getHandler(path);
			}
			catch(...)
			{
				// Unlock the mutex and rethrow.
				unlock_mutex(&treeMutex);
				unlock_mutex(&getEntMutex);

				throw;
			}

			unlock_mutex(&treeMutex);

			AttrMap amap;
			parseAttrs(attrs, amap);

			PathVector pvec = vectorizePath(path);

			Entity ent = h->handlePath(*this, pvec, amap);

			T *ret;
			try
			{
				ret = any_cast<T*>(ent);;
			}
			catch(bad_any_cast &e)
			{
				unlock_mutex(&getEntMutex);

				string bad = "BADTYPE getMutableEntity/2: The HANDLER gave us a type of object you were not expecting. ";
				bad += e.what();
				throw Exception(Exception::BADTYPE, bad);
			}

			if(ret == NULL)
			{
				unlock_mutex(&getEntMutex);

				string bad = "NOENT getMutableEntity/2: The handler gave us a null value.";
				throw Exception(Exception::NOENT, bad);
			}
			else
			{
			    return CloakRef<T>(ret);
			}
		}


		/**
			\brief Get a CloakRef that points to the requested
			mutable entity.

			The returned entity is owned by the caller, and can be
			modified.

			It will automatically be deleted when the last CloakRef
			to it is destroyed.

			This is the method you will use to obtain a private copy
			of an entity so that you may modify it. Returned
			entities are not cached. Use this method if you want
			to use Cloak as a Simple Factory.

			@note This is thread safe.

		*/
		template<typename T>
		CloakRef<T> getEntityCopy(const string &path, const string &attrs)
		{

		    try {
		    	    CloakRef<const T> ref = this->getEntity<T>(path,attrs);
		    	    T *temp = new T(*ref);
		    	    return CloakRef<T>(temp);
		    }
		    catch(Exception e) {
		        string bad = "BADTYPE getEntityCopy/2: The HANDLER failed to find the requested entity. ";
		        throw Exception(e.getValue(), bad);
		    }
		}


		/**
			\brief Debug the tree

			This function just dumps the entity tree to stderr.

			Use this for testing.

			@note This is thread safe.
		*/
		void debugTree();

		/**
			\brief Debug the cache

			This function dumps the cache to stderr.

			Use this for testing.

			@note This is thread safe.
		*/
		void debugCache();

	private:
		/// Just to pass data to the garbage thread
		typedef struct garbage_thread_data {
			Manager *man;
			GarbageMode mode;
			int time;
		} garbage_thread_data_t;
	};

}

#endif
